package com.gitee.kamismile.stoneComEx.common.component.support.servlet;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.gitee.kamismile.stone.commmon.util.ValueUtils;
import com.gitee.kamismile.stoneComEx.common.component.servlet.ServletLocaleResolver;
import com.gitee.kamismile.stoneComEx.common.component.support.annotation.XssParameter;
import com.gitee.kamismile.stoneComEx.common.filter.servlet.LocaleChangeInterceptor;
import com.gitee.kamismile.stoneComEx.common.filter.servlet.LoggingInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.util.HtmlUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;


/**
 * Created by lidong on 15-9-11.
 */
@Configuration
@DependsOn("messageSource")
//@Component
public class SimpleWebConfig extends DelegatingWebMvcConfiguration {
//
//    @Bean
//    public RequestMappingHandlerMapping requestMappingHandlerMapping(FormattingConversionService mvcConversionService,
//                                                                     ResourceUrlProvider mvcResourceUrlProvider) {
//        RequestMappingHandlerMapping handlerMapping = new SimpleRequestMappingHandlerMapping();
//        handlerMapping.setOrder(0);
//        handlerMapping.setInterceptors(getInterceptors(mvcConversionService,mvcResourceUrlProvider));
//        handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
//
//        PathMatchConfigurer configurer = getPathMatchConfigurer();
//        if (configurer.isUseSuffixPatternMatch() != null) {
//            handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
//        }
//        if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
//            handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
//        }
//        if (configurer.isUseTrailingSlashMatch() != null) {
//            handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
//        }
//        if (configurer.getPathMatcher() != null) {
//            handlerMapping.setPathMatcher(configurer.getPathMatcher());
//        }
//        if (configurer.getUrlPathHelper() != null) {
//            handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
//        }
//
//        return handlerMapping;
//    }


    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper());
        converter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "html", Charset.forName("UTF-8")),
                new MediaType("application", "json", Charset.forName("UTF-8")))
        );
        return converter;
    }

//    @Bean
//    public GsonHttpMessageConverter GsonHttpMessageConverter(){
//        Gson gson =new GsonBuilder()
////                .registerTypeAdapter(String.class, new StringConverter())
//                .create();
//
//        final GsonHttpMessageConverter converter = new GsonHttpMessageConverter(gson);
//        converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8,
//               MediaType.TEXT_HTML));
//        return converter;
//    }

    @Bean
    public StringHttpMessageConverter StringHttpMessageConverter() {
        final StringHttpMessageConverter converter = new StringHttpMessageConverter();
        converter.setSupportedMediaTypes(Arrays.asList(new MediaType("text", "html", Charset.forName("UTF-8"))));
        return converter;
    }


//    @Bean
//    @Primary
//    @ConditionalOnClass(ReloadableResourceBundleMessageSource.class)
//    @ConditionalOnMissingBean(ReloadableResourceBundleMessageSource.class)
//    public ReloadableResourceBundleMessageSource messageSource() {
//        ReloadableResourceBundleMessageSource rbms = new ReloadableResourceBundleMessageSource();
//        rbms.setDefaultEncoding("UTF-8");
//        rbms.setBasename("classpath:messages/messages");
//        rbms.setUseCodeAsDefaultMessage(true);
//        return rbms;
//    }

    @Autowired
    private ReloadableResourceBundleMessageSource messageSource;

    @Override
    protected Validator getValidator() {
        OptionalValidatorFactoryBean optionalValidatorFactoryBean=new OptionalValidatorFactoryBean();
        optionalValidatorFactoryBean.setValidationMessageSource(messageSource);
        return optionalValidatorFactoryBean;
    }



    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(StringHttpMessageConverter());
        converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2HttpMessageConverter); // 删除MappingJackson2HttpMessageConverter
        converters.add(mappingJackson2HttpMessageConverter());
//        converters.add(GsonHttpMessageConverter());
    }

    @Bean
    @ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
    public ServletLocaleResolver localeResolver(){
        return new ServletLocaleResolver();
    }


    @Bean
    public  LocaleChangeInterceptor localeChangeInterceptor(){
        return new LocaleChangeInterceptor(localeResolver());
    }
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
        registry.addInterceptor(new LoggingInterceptor());
        super.addInterceptors(registry);
    }

//    @Override
//    public void addViewControllers(ViewControllerRegistry registry) {
//        registry.addStatusController("/error",HttpStatus.INTERNAL_SERVER_ERROR);
//    }

//    class StringConverter implements JsonSerializer<String> {
//        public JsonElement serialize(String src, Type typeOfSrc,
//                                     JsonSerializationContext context) {
//            if (src == null) {
//                return new JsonPrimitive("");
//            } else {
//                return new JsonPrimitive(src.toString());
//            }
//        }
//      }



    @Autowired
    private Jackson2ObjectMapperBuilder builder;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Bean
    @Primary
    @ConditionalOnClass(ObjectMapper.class)
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = builder.build();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//        objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
//        objectMapper.registerModule(new JavaTimeModule());
        objectMapper
                .setSerializerFactory(objectMapper
                        .getSerializerFactory()
                        .withSerializerModifier(new FastJsonSerializerFeatureCompatibleForJackson()));
        return objectMapper;
    }

    private class FastJsonSerializerFeatureCompatibleForJackson extends BeanSerializerModifier {
        private JsonSerializer<Object> nullStringJsonSerializer = new NullStringJsonSerializer();
        private JsonSerializer<Object> nullNumberJsonSerializer = new NullNumberJsonSerializer();
        private JsonSerializer<Object> nullObjectJsonSerializer = new NullObjectJsonSerializer();
        private JsonSerializer<Object> nullListJsonSerializer = new NullListJsonSerializer();
        private JsonSerializer<Object> xssStringJsonSerializer = new XssStringJsonSerializer();


        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
                                                         List<BeanPropertyWriter> beanProperties) {
            // 循环所有的beanPropertyWriter
            beanProperties.stream().forEach(writer -> {
                JavaType propertyType = writer.getMember().getType();
                XssParameter xssParameter = writer.getAnnotation(XssParameter.class);
                Class<?> clazz = propertyType.getRawClass();
                if (clazz.equals(String.class)) {

                    if (ValueUtils.isNull(xssParameter)) {
                        writer.assignSerializer(this.xssStringJsonSerializer);
                    } else if (ValueUtils.isNotNull(xssParameter) && xssParameter.xss()) {
                        writer.assignSerializer(this.xssStringJsonSerializer);
                    }

                    writer.assignNullSerializer(this.nullStringJsonSerializer);

                } else if (clazz.equals(Date.class)) {
                    writer.assignNullSerializer(this.nullStringJsonSerializer);
                } else if (clazz.equals(Integer.class) || clazz.equals(Long.class) ||
                        clazz.equals(Double.class) || clazz.equals(Float.class) ||
                        (Number.class.isAssignableFrom(clazz))) {
                    writer.assignNullSerializer(this.nullNumberJsonSerializer);
                } else if (clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class) || propertyType.isCollectionLikeType()) {
                    writer.assignNullSerializer(this.nullListJsonSerializer);
                } else {
                    //给writer注册一个自己的nullSerializer
                    writer.assignNullSerializer(this.nullObjectJsonSerializer);
                }

            });


            return beanProperties;
        }


        private class NullStringJsonSerializer extends JsonSerializer<Object> {
            @Override
            public void serialize(Object arg0, JsonGenerator arg1,
                                  SerializerProvider arg2) throws IOException, JsonProcessingException {
                if (ValueUtils.isNull(arg0)) {
                    arg1.writeString("");
                } else {
                    arg1.writeObject(arg0);
                }
            }
        }


        private class NullListJsonSerializer extends JsonSerializer<Object> {
            @Override
            public void serialize(Object arg0, JsonGenerator arg1,
                                  SerializerProvider arg2) throws IOException, JsonProcessingException {
                if (ValueUtils.isNull(arg0)) {
                    arg1.writeStartArray();
                    arg1.writeEndArray();
//                    arg1.writeObject(null);
                } else {
                    arg1.writeObject(arg0);
                }
            }
        }


        private class NullObjectJsonSerializer extends JsonSerializer<Object> {
            @Override
            public void serialize(Object arg0, JsonGenerator arg1,
                                  SerializerProvider arg2) throws IOException, JsonProcessingException {
                if (ValueUtils.isNull(arg0)) {
//                    arg1.writeStartObject();
//                    arg1.writeEndObject();
                    arg1.writeObject(null);
                } else {
                    arg1.writeObject(arg0);
                }
            }
        }

        private class XssStringJsonSerializer extends JsonSerializer<Object> {

            @Override
            public void serialize(Object value, JsonGenerator jsonGenerator,
                                  SerializerProvider serializerProvider) throws IOException {
                if (ValueUtils.isNotNull(value)) {
                    String encodedValue = HtmlUtils.htmlEscape(ValueUtils.isStringNull(value.toString()));
                    jsonGenerator.writeString(encodedValue);
                } else {
                    jsonGenerator.writeString("");
                }
            }

        }

        private class NullNumberJsonSerializer extends JsonSerializer<Object> {
            @Override
            public void serialize(Object arg0, JsonGenerator arg1,
                                  SerializerProvider arg2) throws IOException, JsonProcessingException {
                if (ValueUtils.isNull(arg0)) {
                    arg1.writeObject(0);
                } else {
                    arg1.writeObject(arg0);
                }
            }
        }
    }
}

